#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <cstdlib>
#include <unistd.h>
#include <memory>
#include "log.hpp"
#include "InetAddr.hpp"

namespace net
{
    class Socket;
    using socket_ptr = std::shared_ptr<Socket>;

    enum
    {
        SOCKET_ERROR,
        BIND_ERROR,
        LISTEN_ERROR,
        ACCEPT_ERROR,
        CONNECECT_ERROR,
    };

    // 模板方法模式
    class Socket
    {
    public:
        virtual void CreateSocketOrDie() = 0;
        virtual void BindSocketOrDie(InetAddr &addr) = 0;
        virtual void ListenSocketOrDie() = 0;
        virtual socket_ptr Accept(InetAddr *addr) = 0;
        virtual bool Connetcor(InetAddr &addr) = 0;
        virtual int SockFd() = 0;
        virtual int Recv(std::string *out) = 0;
        virtual int Send(const std::string &in) = 0;
        virtual void Close() = 0;
        virtual void SetSocketAddrReuse() = 0;

        void BuildServerSocket(InetAddr &addr)
        {
            CreateSocketOrDie();
            SetSocketAddrReuse();
            BindSocketOrDie(addr);
            ListenSocketOrDie();
        }

        bool BuildClentSocket(InetAddr &addr)
        {
            std::cout<<"BuildClentSocket"<<std::endl;
            CreateSocketOrDie();
            return Connetcor(addr);
        }
    };

    class TcpSocket : public Socket
    {
    public:
        TcpSocket(int sockfd = -1) : _sockfd(sockfd)
        {
        }

        virtual void CreateSocketOrDie() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                Log(FATAL, "socket create failed:%s", strerror(errno));
                exit(SOCKET_ERROR);
            }
            Log(DEBUG, "socket create success");
        }

        virtual void BindSocketOrDie(InetAddr &addr) override
        {
            struct sockaddr_in t;
            bzero(&t, sizeof(t));
            t.sin_family = AF_INET;
            t.sin_port = htons(addr.port());
            t.sin_addr.s_addr = inet_addr(addr.ip().c_str());
            int ret = bind(_sockfd, (struct sockaddr *)&t, sizeof(t));
            if (ret < 0)
            {
                Log(FATAL, "bind failed:%s", strerror(errno));
                exit(BIND_ERROR);
            }
            Log(DEBUG, "bind success");
        }

        virtual void ListenSocketOrDie() override
        {
            int ret = listen(_sockfd, 5);
            if (ret < 0)
            {
                Log(FATAL, "listen failed:%s", strerror(errno));
                exit(LISTEN_ERROR);
            }
            Log(DEBUG, "listen success");
        }

        virtual socket_ptr Accept(InetAddr *addr) override
        {
            struct sockaddr_in t;
            socklen_t len = sizeof(len);
            int sockfd = accept(_sockfd, (struct sockaddr *)&t, &len);
            if (sockfd < 0)
            {
                Log(WARNING, "accept failed:%s", strerror(errno));
                return nullptr;
            }
            *addr = t;
            socket_ptr sock = std::make_shared<TcpSocket>(sockfd);
            return sock;
        }

        virtual bool Connetcor(InetAddr &addr) override
        {
            struct sockaddr_in t;
            bzero(&t, sizeof(t));
            t.sin_addr.s_addr = inet_addr(addr.ip().c_str());
            t.sin_family = AF_INET;
            t.sin_port = htons(addr.port());
            std::cout<<"Connetcor:"<<_sockfd<<std::endl;

            int ret = connect(_sockfd, (struct sockaddr *)&t, sizeof(t));
            if (ret < 0)
            {
                Log(WARNING, "connect failed:%s", strerror(errno));
                return false;
            }
            Log(DEBUG, "connect success");
            return true;
        }

        virtual int SockFd() override
        {
            return _sockfd;
        }

        virtual int Recv(std::string *out) override
        {
            char buffer[4096];
            ssize_t n=recv(_sockfd,buffer,sizeof(buffer)-1,0);
            if(n>0)
            {
                buffer[n]='\0';
                *out+=buffer;
            }
            return n;
        }

        virtual int Send(const std::string &in) override
        {
            ssize_t n = send(_sockfd, in.c_str(), in.size(), 0);
            return n;
        }

        virtual void Close() 
        {
            ::close(_sockfd);
        }

        virtual void SetSocketAddrReuse() override
        {
            int opt = 1;
            ::setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        }

    private:
        int _sockfd;
    };

}

